Coverage Report

Created: 2026-02-05 09:02

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
D:\a\scloud-dns\scloud-dns\src\dns\packet\header\mod.rs
Line
Count
Source
1
use crate::exceptions::SCloudException;
2
3
#[derive(Debug, PartialEq, Default)]
4
pub struct Header {
5
    pub id: u16,      // identifier
6
    pub qr: bool,     // 0 for query, 1 for response
7
    pub opcode: u8,   // 0 for standard query
8
    pub aa: bool,     // authoritative answer
9
    pub tc: bool,     // truncated message
10
    pub rd: bool,     // recursion desired
11
    pub ra: bool,     // recursion available
12
    pub z: u8,        // reserved for future use
13
    pub rcode: u8,    // 0 for no error
14
    pub qdcount: u16, // number of entries in the question section
15
    pub ancount: u16, // number of resource records in the answer section
16
    pub nscount: u16, // number of name server resource records in the authority records section
17
    pub arcount: u16, // number of resource records in the additional records section
18
}
19
20
impl Header {
21
    pub(crate) const DNS_HEADER_LEN: usize = 12;
22
23
    /// Serialize the DNS header into a byte array
24
    ///
25
    /// # Exemple :
26
    /// ```
27
    /// use crate::dns::packet::header::Header;
28
    ///
29
    /// let header = Header {
30
    ///     id: 0x1234,
31
    ///     qr: false,
32
    ///     opcode: 0,
33
    ///     aa: false,
34
    ///     tc: false,
35
    ///     rd: true,
36
    ///     ra: false,
37
    ///     z: 0,
38
    ///     rcode: 0,
39
    ///     qdcount: 1,
40
    ///     ancount: 0,
41
    ///     nscount: 0,
42
    ///     arcount: 0,
43
    /// };
44
    ///
45
    /// let bytes = header.to_bytes().unwrap();
46
    ///
47
    /// assert_eq!(bytes.len(), Header::DNS_HEADER_LEN);
48
    /// assert_eq!(bytes[0], 0x12);
49
    /// assert_eq!(bytes[1], 0x34);
50
    /// ```
51
5
    pub fn to_bytes(&self) -> Result<[u8; Self::DNS_HEADER_LEN], SCloudException> {
52
5
        let mut bytes = [0u8; Self::DNS_HEADER_LEN];
53
54
5
        let id_bytes = self.id.to_be_bytes();
55
5
        bytes[0] = id_bytes[0];
56
5
        bytes[1] = id_bytes[1];
57
58
5
        let mut flags1 = 0u8;
59
5
        flags1 |= (self.qr as u8 & 0x1) << 7;
60
5
        flags1 |= (self.opcode & 0xF) << 3;
61
5
        flags1 |= (self.aa as u8 & 0x1) << 2;
62
5
        flags1 |= (self.tc as u8 & 0x1) << 1;
63
5
        flags1 |= self.rd as u8 & 0x1;
64
65
5
        let mut flags2 = 0u8;
66
5
        flags2 |= (self.ra as u8 & 0x1) << 7;
67
5
        flags2 |= (self.z & 0x7) << 4;
68
69
5
        bytes[2] = flags1;
70
5
        bytes[3] = flags2;
71
72
5
        let qdcount_bytes = self.qdcount.to_be_bytes();
73
5
        bytes[4] = qdcount_bytes[0];
74
5
        bytes[5] = qdcount_bytes[1];
75
76
5
        let ancount_bytes = self.ancount.to_be_bytes();
77
5
        bytes[6] = ancount_bytes[0];
78
5
        bytes[7] = ancount_bytes[1];
79
80
5
        let nscount_bytes = self.nscount.to_be_bytes();
81
5
        bytes[8] = nscount_bytes[0];
82
5
        bytes[9] = nscount_bytes[1];
83
84
5
        let arcount_bytes = self.arcount.to_be_bytes();
85
5
        bytes[10] = arcount_bytes[0];
86
5
        bytes[11] = arcount_bytes[1];
87
88
5
        Ok(bytes)
89
5
    }
90
91
    /// Deserialize the DNS header from a byte array
92
    ///
93
    /// # Exemple :
94
    /// ```
95
    /// use crate::dns::packet::header::Header;
96
    ///
97
    /// let raw_header: [u8; 12] = [
98
    ///     0x12, 0x34, // ID
99
    ///     0x01, 0x00, // Flags (standard query, RD = 1)
100
    ///     0x00, 0x01, // QDCOUNT = 1
101
    ///     0x00, 0x00, // ANCOUNT = 0
102
    ///     0x00, 0x00, // NSCOUNT = 0
103
    ///     0x00, 0x00, // ARCOUNT = 0
104
    /// ];
105
    ///
106
    /// let header = Header::from_bytes(&raw_header).unwrap();
107
    ///
108
    /// assert_eq!(header.id, 0x1234);
109
    /// assert_eq!(header.qr, false);
110
    /// assert_eq!(header.rd, true);
111
    /// assert_eq!(header.qdcount, 1);
112
    /// ```
113
4
    pub fn from_bytes(buf: &[u8]) -> Result<Header, SCloudException> {
114
4
        if buf.len() == 0 {
115
1
            return Err(SCloudException::SCLOUD_HEADER_BYTES_EMPTY);
116
3
        }
117
118
3
        if buf.len() < Header::DNS_HEADER_LEN {
119
1
            return Err(SCloudException::SCLOUD_HEADER_DESERIALIZATION_FAILED);
120
2
        }
121
122
2
        Ok(Header {
123
2
            id: u16::from_be_bytes([buf[0], buf[1]]),
124
2
            qr: (buf[2] & 0b1000_0000) != 0,
125
2
            opcode: (buf[2] & 0b0111_1000) >> 3,
126
2
            aa: (buf[2] & 0b0000_0100) != 0,
127
2
            tc: (buf[2] & 0b0000_0010) != 0,
128
2
            rd: (buf[2] & 0b0000_0001) != 0,
129
2
            ra: (buf[3] & 0b1000_0000) != 0,
130
2
            z: (buf[3] & 0b0111_0000) >> 4,
131
2
            rcode: buf[3] & 0b0000_1111,
132
2
            qdcount: u16::from_be_bytes([buf[4], buf[5]]),
133
2
            ancount: u16::from_be_bytes([buf[6], buf[7]]),
134
2
            nscount: u16::from_be_bytes([buf[8], buf[9]]),
135
2
            arcount: u16::from_be_bytes([buf[10], buf[11]]),
136
2
        })
137
4
    }
138
}